/****************************************************************************
*  Title: stimulus.sc
*  Author: Chandra Garrepally
*  Date: 
*  Description: nsa4400 Specification Model
****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "des.h" 

import "c_queue";
import "c_double_handshake";
import "ofdm";
import "desFunction";
import "desScrunch";
import "desUnscrun";
import "desKeyGen";

// c1: key, c2: data block
behavior Stimulus(i_sender key_s, in event desReady, i_sender data_s)  
{   
  const char* bName = "Stimulus";
  void main(void)  
  {
    const unsigned long blockSize = 8;
    const unsigned long numBlocks = 10;
    const unsigned long dataSize = blockSize * numBlocks;

    unsigned int i = 0;
    unsigned int j = 0;

    // Key
    unsigned char key[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
    short edf = EN0;

    unsigned char data[dataSize];

    // Fill the blocks with data
    for (i = 0; i < dataSize ; i++) { data[i] = i + '0'; }
    
    // Send the key and edf values in a message to the design behavior
    printf("%s: Sending key and edf values to Design...\n", bName);
    key_s.send(&key, sizeof(key));
    key_s.send(&edf, sizeof(edf));

    // Wait for desReady event before sending data.
    wait(desReady);

    // Send the data (words) using the out key_s port to the design behavior.
    for (i = 0, j = 0; i < dataSize && j < numBlocks; i = i + 8, j++)
    {
      puts("------------------------------------");
      printf("%s: Sending block #%d\n", bName, j);
      puts("------------------------------------");
      printf("%s: data[%i..%i]: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x...\n", 
                                bName, i, i + 7, 
                                data[i], data[i+1], data[i+2], data[i+3], 
                                data[i+4], data[i+5], data[i+6], data[i+7]);
      data_s.send(&data[i], blockSize/2);
      data_s.send(&data[i+4], blockSize/2);

      // Arbitrary wait...
      waitfor(1);
    }

    puts("Simulation done....");
    exit(0);
  }
};

behavior Des(i_receiver inputKeys_r, out event desReady, i_receiver inputData_r,  i_sender outputData_s)
{
  // Communication Channels
  c_double_handshake keyGenToDesCh; // to pass keys from key generator to des function
  c_double_handshake scrunchToDesCh; // to pass data from scrunch to des function
  c_double_handshake desToUnscrunchCh; // to pass data from des function to unscrunch 

  // Behavior instances
  desKeyGen keyGeneration(inputKeys_r, keyGenToDesCh);
  desScrunch desScrunching(inputData_r, scrunchToDesCh);
  desFunction desEncryptDecrypt(keyGenToDesCh, desReady, scrunchToDesCh, desToUnscrunchCh);
  desUnscrun desUnscrunching(desToUnscrunchCh, outputData_s);

  void main()
  {
    par
    {
      keyGeneration.main();
      desScrunching.main();
      desEncryptDecrypt.main();
      desUnscrunching.main();
    }
  }
};

behavior Design(i_receiver inputData_r, 
                out event desReady,
                i_receiver inputKeys_r, 
                i_sender outputData_s) 
{
  // Channel instantiation
  c_double_handshake desToOfdmCh;

  // Behavior instantiation
  Des encryption(inputData_r, desReady, inputKeys_r, desToOfdmCh);
  ofdm modulation(desToOfdmCh, outputData_s);

  void main()
  {
    par 
    { 
      encryption.main(); 
      modulation.main();
    }
  }
};

behavior Output(i_receiver ir)
{
  const char* bName = "Output";
  float result_re, result_im;
  void main()
  {
    while(1) {
      ir.receive(&result_re, sizeof(float));
      ir.receive(&result_im, sizeof(float));
      printf("%s: result_re: %f, result.im: %f\n", bName, result_re, result_im);
    }
  }
};

behavior Main()
{
  // Communication elements
  c_double_handshake inputKeyCh, inputDataCh, outputDataCh;

  // Local variables
  float out_re, out_im;

  // Synch. events
  // between Stimulus and Des
  event desReady; 

  // Behavior instantiation
  Stimulus input(inputKeyCh, desReady, inputDataCh);
  Design application(inputKeyCh, desReady, inputDataCh, outputDataCh);
  Output result(outputDataCh);

  int main() 
  {
    par 
    { 
      input.main(); 
      application.main(); 
      result.main(); 
    }
    return 0;
  }
};
